package com.stone.framework.user.service.impl;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.stone.framework.core.cache.UserConstant;
import com.stone.framework.core.exception.BusinessException;
import com.stone.framework.core.util.Beans;
import com.stone.framework.core.util.Passwords;
import com.stone.framework.core.util.RedisUtil;
import com.stone.framework.core.util.Strings;
import com.stone.framework.message.user.LoginInfoReqBody;
import com.stone.framework.message.user.LoginInfoResBody;
import com.stone.framework.message.user.TokenInfoReqBody;
import com.stone.framework.message.user.TokenInfoResBody;
import com.stone.framework.message.user.UserDelReqBody;
import com.stone.framework.message.user.UserPageReqBody;
import com.stone.framework.message.user.UserPageResBody;
import com.stone.framework.message.user.UserPasswordReqBody;
import com.stone.framework.message.user.UserReqBody;
import com.stone.framework.user.entity.User;
import com.stone.framework.user.mapper.UserMapper;
import com.stone.framework.user.service.PermissionService;
import com.stone.framework.user.service.RoleService;
import com.stone.framework.user.service.UserService;

@Service
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    /* @Autowired
    private DeptService deptService;*/

    /*    @Autowired
    private UserDeptMapper userDeptMapper;*/

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public LoginInfoResBody queryOne(LoginInfoReqBody reqBody) {
        User user = userMapper
            .selectOne(new QueryWrapper<User>().eq("name", reqBody.getUsername()).eq("state", User.State.ENABLED));
        LoginInfoResBody resBody = new LoginInfoResBody();
        Beans.copyProperties(user, resBody);
        return resBody;
    }

    @Override
    public TokenInfoResBody queryTokenInfo(TokenInfoReqBody reqBody) {
        List<String> roles = roleService.queryByUsername(reqBody.getUsername());
        List<String> perms = permissionService.queryByUsername(reqBody.getUsername());
        // String deptCode = deptService.queryByUsername(reqBody.getUsername());
        User user = userMapper
            .selectOne(new QueryWrapper<User>().eq("name", reqBody.getUsername()).eq("state", User.State.ENABLED));

        TokenInfoResBody resBody = new TokenInfoResBody();
        resBody.setUsername(reqBody.getUsername());
        resBody.setCname(user.getCname());
        resBody.setRoles(roles);
        resBody.setPermissions(perms);
        resBody.setDeptCode(user.getDeptCode());

        return resBody;
    }

    @Override
    public IPage<UserPageResBody> queryPage(UserPageReqBody reqBody, Page<User> page) {
        User entity = new User();
        Beans.copyProperties(reqBody, entity);
        return userMapper.selectPage(page, this.condition(entity)).convert(e -> {
            UserPageResBody resBody = new UserPageResBody();
            Beans.copyProperties(e, resBody);
            return resBody;
        });
    }

    @Override
    @Transactional(readOnly = false)
    public void save(UserReqBody reqBody) {
        User entity = new User();
        Beans.copyProperties(reqBody, entity);
        Integer count = userMapper.selectCount(new QueryWrapper<User>().eq("name", entity.getName()));
        if (count > 0) {
            throw new BusinessException("用户已存在，请重新输入");
        }

        String salt = Passwords.buildSalt(1000);
        entity.setSalt(salt);

        // 默认使用用户名作为初始密码
        String password = Passwords.encryption(Passwords.encryption(entity.getName()), entity.getSalt());
        entity.setPassword(password);
        userMapper.insert(entity);

        /*UserDept userDept = new UserDept();
        userDept.setDeptCode(reqBody.getDeptCode());
        userDept.setUsername(reqBody.getName());
        userDeptMapper.insert(userDept);*/
    }

    @Override
    @Transactional(readOnly = false)
    @CacheEvict(value = {UserConstant.LOGIN_INFO}, key = "#reqBody.username")
    public void modifyPassword(UserPasswordReqBody reqBody) {
        this.modifyPassword(reqBody, true);
    }

    @Override
    @Transactional(readOnly = false)
    @CacheEvict(value = {UserConstant.LOGIN_INFO}, key = "#reqBody.username")
    public void modifyPassword(UserPasswordReqBody reqBody, boolean checkPassword) {
        User user = userMapper
            .selectOne(new QueryWrapper<User>().eq("name", reqBody.getUsername()).eq("state", User.State.ENABLED));
        if (checkPassword
            && !user.getPassword().equals(Passwords.encryption(reqBody.getOldPassword(), user.getSalt()))) {
            throw new BusinessException("密码错误");
        }
        userMapper.updatePassword(reqBody.getUsername(),
            Passwords.encryption(reqBody.getNewPassword(), user.getSalt()));
    }

    @Override
    @Transactional(readOnly = false)
    @CacheEvict(value = {UserConstant.LOGIN_INFO, UserConstant.TOKEN_INFO}, key = "#reqBody.name")
    public void modify(UserReqBody reqBody) {
        User entity = userMapper.selectById(reqBody.getId());
        if (entity == null) {
            throw new BusinessException("用户不存在");
        }
        Beans.copyProperties(reqBody, entity);
        Integer count = userMapper.selectCount(new QueryWrapper<User>().eq("name", entity.getName()));
        if (count > 1) {
            throw new BusinessException("用户重复，请重新输入");
        }
        userMapper.updateById(entity);

        /*UserDept userDept = new UserDept();
        userDept.setDeptCode(reqBody.getDeptCode());
        userDept.setUsername(reqBody.getName());
        userDeptMapper.update(userDept, new QueryWrapper<UserDept>().eq("username", reqBody.getName()));*/
    }

    @Override
    @Transactional(readOnly = false)
    @CacheEvict(value = {UserConstant.LOGIN_INFO, UserConstant.TOKEN_INFO}, key = "#reqBody.name")
    // TODO 新增annotation，异步删除所有匹配的缓存
    public void delete(UserDelReqBody reqBody) {
        User entity = userMapper.selectById(reqBody.getId());
        if (entity == null) {
            throw new BusinessException("用户不存在");
        }
        entity.setState(User.State.DISABLED.name());
        userMapper.updateById(entity);
    }

    @Override
    public void refresh(List<Long> userIdList) {
        List<User> userList = userMapper.selectBatchIds(userIdList);
        List<String> keys = userList.stream().map(user -> UserConstant.TOKEN_INFO.concat("::").concat(user.getName()))
            .peek(str -> LOGGER.info("清除缓存: {}", str)).collect(Collectors.toList());
        redisUtil.delete(keys);
    }

    private QueryWrapper<User> condition(User condition) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like(Strings.notEmpty(condition.getName()), "name", condition.getName());
        wrapper.like(Strings.notEmpty(condition.getCname()), "cname", condition.getCname());
        wrapper.eq(Strings.notEmpty(condition.getDeptCode()), "dept_code", condition.getDeptCode());
        wrapper.eq(Objects.nonNull(condition.getState()), "state", condition.getState());
        return wrapper;
    }

}
